package com.csf.servlet;

import com.csf.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 中央控制器
 * @author chenshf
 */
public class DispatcherServlet extends HttpServlet {

    /**
     * 存放所有类路径
     */
    private List<String> classNames = new ArrayList<>();
    /**
     * 存放所有实例化的类
     */
    private Map<String, Object> beanMap = new HashMap<>();
    /**
     * 存放请求地址路径
     */
    private Map<String, Method> urlMappingHandler = new HashMap<>();


    @Override
    public void init(ServletConfig config) throws ServletException {
        //扫描所有的bean
        doScanClass("com.csf");
        //根据扫描到的list结果，生成对象(反射对象)
        doInstance();
        //注入依赖关系
        doAutowired();
        //建立地址关系
        urlMapping();
    }

    public void urlMapping() {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            //差点坑死
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(Controller.class)) {
                RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);
                //hello
                String classPath = mapping.value();
                //获取类里的所有方法
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping mapping1 = method.getAnnotation(RequestMapping.class);
                        String methodPath = mapping1.value();
                        //key=hello/sayHello value=sayHello
                        urlMappingHandler.put(classPath + methodPath, method);
                    } else {
                        continue;
                    }
                }

            } else {
                continue;
            }
        }
    }

    public void doAutowired() {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            //差点坑死
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(Controller.class)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        String key = autowired.value();
                        //成员变量私有权限打开
                        field.setAccessible(true);
                        try {
                            //完成值的注入，
                            field.set(instance, beanMap.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }

                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }

    public void doInstance() {
        for (String className : classNames) {
            //去掉后缀名得到全限路径：com.csf.controller.HelloController
            String cn = className.replace(".class", "");
            try {
                Class<?> clazz = Class.forName(cn);
                //筛选带有@Controller注解和@Service注解的
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //扫描到的是控制类
                    Object instance = clazz.newInstance();
                    RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);
                    String key = mapping.value();
                    beanMap.put(key, instance);
                } else if (clazz.isAnnotationPresent(Service.class)) {
                    //得到的是一个服务类
                    Object instance = clazz.newInstance();
                    Service service = clazz.getAnnotation(Service.class);
                    String key = service.value();
                    beanMap.put(key, instance);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    public void doScanClass(String basePackage) {
        //扫描所有类路径
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        String rootPath = url.getPath();
        File file = new File(rootPath);
        String[] filesStr = file.list();
        for (String path : filesStr) {
            File filePath = new File(rootPath + path);
            if (filePath.isDirectory()) {
                doScanClass(basePackage + "." + path);
            } else {
                //找到了.class结尾的类
                classNames.add(basePackage + "." + filePath.getName());
            }
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /* 请求路径：my-springmvc/hello/sayHello */
        String uri = req.getRequestURI();
        //拿到 my-springmvc
        String context = req.getContextPath();
        //把my-springmvc替换为空 得到具体的请求路径
        String path = uri.replace(context, "");
        Method method = urlMappingHandler.get(path);
        Object instance = beanMap.get("/" + path.split("/")[1]);
        if(method == null){
            PrintWriter writer = resp.getWriter();
            writer.print("Http code :404 page not fund ,please check your url!");
            writer.flush();
            writer.close();
            return;
        }
        Object[] args = handlerParameter(req, resp, method);
        try {
            method.invoke(instance, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理参数
     * @param request
     * @param response
     * @param method
     * @return
     */
    private static Object[] handlerParameter(HttpServletRequest request, HttpServletResponse response, Method method) {
        //拿到当前执行方法有哪些参数
        Class<?>[] paramsClazz = method.getParameterTypes();
        //根据参数个数new一个参数数组，将方法所有参数赋值到args来
        Object[] args = new Object[paramsClazz.length];
        int ai = 0;
        int index = 0;
        for (Class<?> paramClazz : paramsClazz) {
            if (ServletRequest.class.isAssignableFrom(paramClazz)) {
                args[ai++] = request;
            }else if (ServletResponse.class.isAssignableFrom(paramClazz)) {
                args[ai++] = response;
            }else {
                Annotation[] annotations = method.getParameterAnnotations()[index];
                if (annotations.length > 0) {
                    for (Annotation annotation : annotations) {
                        if (RequestParam.class.isAssignableFrom(annotation.getClass())) {
                            RequestParam rp = (RequestParam) annotation;
                            args[ai++] = request.getParameter(rp.value());
                        }
                    }
                }
            }
            index++;
        }
        return args;
    }

}
